#include "impl.h"
#include "cfg.h"
#include "common.h"
#include "dist.h"
#include "formula.h"
#include "sleep_api.h"
#include "sleep_share.h"
#include "vector.h"
#include <stddef.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <time.h>

/*
编译脚本根据环境变量选择相应的策略对象
 */
static dist_action_t *_dist = &ver_sel;

static uint32_t _p2u32(const void *v) { return *((uint32_t *)v); }
/*
sort for wake list
 */
NEW_CMP(_wake_sort, uint32_t, _p2u32)

/*
@param count: 历史数据长度
 */
static linked_list_t *
_find_sleep_start_end_point(const size_t *index_arr, size_t len_idx,
                            const seg_info_t *seg_results, size_t len_result,
                            const smp_hr_t *hr_list, size_t count) {
  size_t begin, end, prev_index = 0;
  size_t index_start, index_end;

  size_t x;

  linked_list_t *sleep_list = NULL;
  new_linklist(&sleep_list);

  linked_list_t *v4_list = NULL;
  new_linklist(&v4_list);
  ssize_t last_end = -1;
  NOTUSED(last_end);

  for (size_t i = 0; i < len_idx; i++) {
    x = *(index_arr + i);
    if (0 == i) {
      begin = x;
      continue;
    }

    prev_index = *(index_arr + i - 1);
    if (1 != x - prev_index) {
      end = prev_index;
      SLEEP_START_END_CAL_NEW()
      begin = x;
      continue;
    }
  }

  /*
  针对consecutive函数的修复
  当前元素必然比前序号要大
  因此没必要判定

  另外当序号跳跃时
  最后的序号要纳入计算
   */

  end = x;
  SLEEP_START_END_CAL_NEW()

  /*
  在java实现中
  consec链表循环完才开始对sleep start的调用
  此处保持一致
   */
  POST_CALL_START_END_IMPL()

  return sleep_list;
}

static linked_list_t *_find_sleep_range(const smp_hr_t *hr_list, size_t count) {
  seg_info_t *seg_results = (seg_info_t *)malloc(sizeof(seg_info_t) * count);
  if (!seg_results) {
    return NULL;
  }
  size_t len_seg;
  _dist->segment_calc(hr_list, count, SEGMENTED_CALCULATION_TIME, seg_results,
                      &len_seg);

  size_t *index_arr = malloc(sizeof(size_t) * count);

  size_t j = 0;

  for (size_t idx = 0; idx < len_seg; idx++) {
    const seg_info_t *result = seg_results + idx;
    if (!result->valid) {
      continue;
    }

    if (_dist->sleep_range_cond(result)) {
      index_arr[j++] = idx;
    }
  }

  linked_list_t *point = NULL;
  if (j) {
    point = _find_sleep_start_end_point(index_arr, j, seg_results, len_seg,
                                        hr_list, count);
  }
  free(seg_results);
  free(index_arr);

  return point;
}

/*
kt源实现中
wakeList.removeAll(sList)

wakeList为list的浅拷贝
那么removeAll等同于追加不属于pair的剩余元素

@param wake_hr_list: 需填充的列表
@param iw_begin: include_to_wake begin
  初始化为0
@param next_iw_begin: next include_to_wake begin
  用pair的end做填充
 */
static void _gen_data(const sleep_start_end_t *pair, const smp_hr_t *list,
                      alias_acts_ll_t *node, uint32_t *wake_hr_list,
                      size_t *include_index, size_t iw_begin,
                      size_t *next_iw_begin) {
  size_t count = pair->end_index - pair->start_index + 1;

  size_t local_include_index = *include_index;
  for (size_t i = iw_begin; i < pair->start_index; i++) {
    *(wake_hr_list + (local_include_index++)) = (list + i)->rate;
  }
  *include_index = local_include_index;
  *next_iw_begin = count + pair->start_index;

  uint32_t *slist = malloc(sizeof(uint32_t) * count);

  for (size_t i = 0; i < count; i++) {
    *(slist + i) = (list + i + pair->start_index)->rate;
  }

  if (!_dist->is_wear(list + pair->start_index, slist, count)) {
    memset(node, 0x00, sizeof(*node));
  } else {
    double avg_hr = avg_uint32_t(slist, count);

    uint64_t begin = (list + pair->start_index)->ts;
    begin -= get_smp_interval() * 60 * 1000;
    uint64_t end = (list + pair->end_index)->ts;

    linked_list_t *staging_out = NULL;
    new_linklist(&staging_out);

    if (end > begin + DEF_EVAL(_dist, STAGING_LIGHT_SLEEP_TIME)) {
      sleep_staging(_dist, begin, list + pair->start_index, count, staging_out);
    } else {
      each_activity_t *s = (each_activity_t *)malloc(sizeof(each_activity_t));
      s->type = ENUM_SLEEP_STAGING_TYPE_NREM1;
      s->begin = begin;
      s->end = end;
      append_item(staging_out, s, NULL);
    }

    node->start_time = begin;
    node->end_time = end;
    node->avg_hr = avg_hr;
    node->slist = staging_out;
  }
  free(slist);
}

/*
因未佩戴而产生的无效数据
 */
static uint8_t _inv_staging(const alias_acts_ll_t *it) {
  return !it || (!it->start_time && !it->end_time && !it->slist);
}

/*
填充分段，顺便计算未睡眠节点
@param tot_len: 源样本总数量
@param sleep_range_cont: count of sleep range linked list
 */
static alias_acts_ll_t *_fill_stagings(const smp_hr_t *list, size_t tot_len,
                                       linked_list_t *point,
                                       double low_data_avg,
                                       double *out_wake_hr_avg,
                                       size_t *out_point_count) {
  size_t cnt_p = count_items(point, NULL);
  *out_point_count = cnt_p;

  if (!cnt_p)
    return NULL;

  alias_acts_ll_t *shot_list =
      (alias_acts_ll_t *)malloc(sizeof(alias_acts_ll_t) * cnt_p);

  /*
  临时用于计算wake hr avg
   */
  uint32_t *wake_hr_list = malloc(sizeof(uint32_t) * tot_len);
  size_t wake_begin = 0;
  size_t wake_include_index = 0;

  for (size_t i = 0; i < cnt_p; i++) {
    sleep_start_end_t *tp[1];
    if (!pop_item_count(point, 1, (void **)tp, NULL, 0)) {
      break;
    }

    /*
    对于未佩戴的情况
    相应节点的各属性为nil
     */
    _gen_data(*tp, list, shot_list + i, wake_hr_list, &wake_include_index,
              wake_begin, &wake_begin);
    free(*tp);
  }

  for (size_t i = wake_begin; i < tot_len; i++) {
    *(wake_hr_list + (wake_include_index++)) = (list + i)->rate;
  }

  double wake_hr_avg = 0;
  if (wake_include_index < 6) {
    wake_hr_avg = low_data_avg * 1.15;
  } else {
    /*
    排序
    取中间1/3求平均值
     */
    qsort(wake_hr_list, wake_include_index, sizeof(uint32_t), _wake_sort);
    size_t begin = wake_include_index / 3;
    size_t end = wake_include_index - begin;

    wake_hr_avg = avg_uint32_t(wake_hr_list + begin, end - begin);
  }

  free(wake_hr_list);
  *out_wake_hr_avg = wake_hr_avg;

  return shot_list;
}

static uint64_t _proc(alias_acts_ll_t *it, uint64_t last_sleep_end_time,
                      linked_list_t *result_list, double wake_hr_avg) {
  if (_inv_staging(it)) {
    /*
    未佩戴的情况
    字段均为0/nil
     */
    return 0;
  }

  if (!(0 != last_sleep_end_time &&
        it->start_time - last_sleep_end_time <=
            DEF_EVAL(_dist, SLEEP_RANGE_MERGE_TIME))) {
    append_item(result_list, it, NULL);
    return it->end_time;
  }

  alias_acts_ll_t *last_data = index_item(result_list, -1, NULL);
  if (_dist->count4wake() && last_data->avg_hr >= wake_hr_avg) {
    /*
    源实现中是新追加
    此处实现为拷贝

    引用了shot的slist内存

     */
    /*
    拷贝替换了原有slist指针
    导致引用丢失
     */
    free_items(last_data->slist, NULL, 0, NULL);
    memcpy(last_data, it, sizeof(*it));
  } else {
    if (_dist->proc_cond(last_data, it->start_time)) {
      each_activity_t *new = (each_activity_t *)malloc(sizeof(each_activity_t));
      new->type = ENUM_SLEEP_STAGING_TYPE_WAKE;
      new->begin = last_data->end_time;
      new->end = it->start_time;

      append_item(last_data->slist, new, NULL);
    }

    while (1) {
      each_activity_t *temp[1];
      if (!pop_item_count(it->slist, 1, (void **)temp, NULL, 0)) {
        free_items(it->slist, NULL, 0, NULL);
        break;
      }
      append_item(last_data->slist, temp[0], NULL);
    }

    /*
    原实现为删除尾节点
    并重新追加对象

    此处为不删除
    而仅更改尾节点的相应属性
     */
    last_data->end_time = it->end_time;
    last_data->avg_hr = (last_data->avg_hr + it->avg_hr) / 2.0;
  }

  return it->end_time;
}

/*
跳过acc异常的区间
 */
static uint8_t _okfunc_acc(const smp_hr_t *list, size_t len, uint64_t begin,
                           uint64_t end) {
  ssize_t open = -1, close = -1;
  for (size_t i = 0; i < len; i++) {
    const smp_hr_t *each = list + i;
    if (-1 == open && begin == each->ts) {
      open = i;
    }
    if (-1 == close && end == each->ts) {
      close = i;
    }

    if (-1 != open && -1 != close) {
      break;
    }
  }

  if (-1 != open && -1 != close) {
    if (_dist->is_acc_func(list + open, close - open + 1)) {
      return 1;
    }
  } else {
    return 1;
  }

  return 0;
}

void sleep_impl(const smp_hr_t *src_list, size_t len, sleep_root **slp_root) {
  _dist->csem_ext = NULL;
  if (!slp_root) {
    return;
  }

  smp_hr_t *list = _dist->pre_proc(src_list, len, &len);

  double low_data_avg;
  double all_data_avg = all_avg(list, len, &low_data_avg);

  linked_list_t *point = _find_sleep_range(list, len);

  sleep_root *target = (sleep_root *)malloc(sizeof(sleep_root));
  *slp_root = target;

  target->avg_hr = all_data_avg;
  target->resting_hr = low_data_avg;

  if (!point) {
    target->summaries = NULL;
    target->count = 0;
    _dist->pre_release(list);
    return;
  }

  size_t cnt_point;
  double wake_hr_avg;

  alias_acts_ll_t *shot_list =
      _fill_stagings(list, len, point, low_data_avg, &wake_hr_avg, &cnt_point);

  uint64_t last_sleep_end_time = 0;

  linked_list_t *result_list = NULL;
  new_linklist(&result_list);

  for (size_t i = 0; i < cnt_point; i++) {
    alias_acts_ll_t *it = shot_list + i;
    if (_inv_staging(it)) {
      continue;
    }

    last_sleep_end_time =
        _proc(it, last_sleep_end_time, result_list, wake_hr_avg);
  }

  size_t smy_count = count_items(result_list, NULL);
  activity_summary_t *smy_list = NULL;
  if (smy_count > 0) {
    smy_list =
        (activity_summary_t *)malloc(sizeof(activity_summary_t) * smy_count);
  }

  target->count = smy_count;
  target->summaries = smy_list;

  size_t si = 0;
  for (size_t access = 0; access < smy_count; access++) {
    alias_acts_ll_t *t[1];
    if (!pop_item_count(result_list, 1, (void **)t, NULL, 0)) {
      break;
    }

    alias_acts_ll_t *data_obj = *t;

    if (data_obj->end_time - data_obj->start_time <=
        DEF_EVAL(_dist, SLEEP_FRAGMENT_MIN_NAP_TIME)) {
      free_items(data_obj->slist, NULL, 1, NULL);

      each_activity_t *new = (each_activity_t *)malloc(sizeof(each_activity_t));
      new->begin = data_obj->start_time;
      new->end = data_obj->end_time;
      new->type = ENUM_SLEEP_STAGING_TYPE_NAP;

      append_item(data_obj->slist, new, NULL);
    }

    if (!_okfunc_acc(list, len, data_obj->start_time, data_obj->end_time)) {
      free_items(data_obj->slist, NULL, 0, NULL);
      target->count--;
      continue;
    }

    activity_summary_t *as = smy_list + si;

    as->begin = data_obj->start_time;
    as->end = data_obj->end_time;
    as->avg_hr = data_obj->avg_hr;

    as->act_list = iter_copy(data_obj->slist, sizeof(each_activity_t),
                             &as->cnt_acts, NULL);
    free_items(data_obj->slist, NULL, 0, NULL);
    si++;
  }

  /*
  关于data对象的内存释放
  由于result直接引用了shot的节点内存

  1. result链表free时,只能释放it的slist内存
  2. 必须通过shot释放内存
   */

  free(shot_list);
  free_items(point, NULL, 0, NULL);
  free_items(result_list, NULL, 0, NULL);

  _dist->pre_release(list);
}